// 需要先引入jquery
(function (window, document) {
  // 外部调用
  var midate = {
    v: '1.0.0' // midate 版本号
    , config: {
      weekStart: 0, // 默认周日一周的开始
    } // 全局配置项
    , format : {
      year: 'yyyy' // 尚不支持
      ,month: 'yyyy-MM' // 尚不支持
      ,date: 'yyyy-MM-dd' // 目前支持
      ,datehour: 'yyyy-MM-dd hh' // 目前支持
      ,dateminute: 'yyyy-MM-dd hh:mm' // 目前支持
      ,time: 'hh:mm:ss' // 尚不支持
      ,datetime: 'yyyy-MM-dd hh:mm:ss' // 目前支持
    }
    , index: (window.midate && window.midate.v) ? 100000 : 0

    // 设置全局项
    , set: function (options) {
      var that = this;
      that.config = $.extend({}, that.config, options);
      return that;
    }
  };

  // 操作当前实例
  var thisModule = function () {
    var that = this
      , options = that.config
      , id = options.id;
    thisModule.that[id] = that; //记录当前实例对象
    console.log("实例thisModule.that：", thisModule.that);
    console.log("调用实例：", ",options:", options)
    return that.inst = {
      //提示框
      hint: function (content) {
        that.hint.call(that, content);
      }
      , config: that.config
    };
  };

  // 组件构造器
  var Class = function (options) {
    var that = this;
    that.index = ++midate.index;
    that.config = $.extend({}, that.config, midate.config, options);

    // 若 elem 非唯一，则拆分为多个实例
    var elem = $(options.elem || that.config.elem);
    console.log("*************************分隔符*************************")
    console.log("组件构造器调用：","elem.length:",elem.length, 'that:',that)
    if (elem.length > 1) {
      $(elem).each(function () {
        console.log("渲染开始")
        midate.render($.extend({}, that.config, {
          elem: this
        }));
      });
      return that;
    }
    options = $.extend(that.config,{});
    options.id = ('id' in options) ? options.id : that.index;
    console.log("组件构造options：",options,",that.config:",that.config)
    that.init();
  };

  // 默认配置
  Class.prototype.config = {
    type: 'date' // 控件类型，支持：year/month/date/time/datetime
    , range: false // 是否开启范围选择，即双控件
    , format: 'yyyy-MM-dd' //默认日期格式
    , value: null // 默认日期，支持传入new Date()，或者符合format参数设定的日期格式字符
    , isInitValue: true // 用于控制是否自动向元素填充初始值（需配合 value 参数使用）
    , min: null // 有效最小日期，年月日必须用“-”分割，时分秒必须用“:”分割 格式必须为type对应的format的默认格式。 例：1900-01-01(type为date) 1900-01-01 00:00:00(type为datetime)
    , max: null // 有效最大日期，同上
    , setMin: function (value) { // 设置最小有效日期， 若value为null，则赋值null
      if (value == null) {this.min = null; this.minObj = null};
      if (!_common.checkDate(value, this.type)) {throw(`当前日期格式为${this.type},参数应该遵循该格式！`);}
      this.minObj = _common.setMinMax(value);
      this.min = value;
    }
    , setMax: function (value) { // 设置最大有效日期， 若value为null，则赋值null
      if (value == null) {this.max = null; this.maxObj = null};
      if (!_common.checkDate(value, this.type)) {throw(`当前日期格式为${this.type},参数应该遵循该格式！`);}
      this.maxObj = _common.setMinMax(value);
      this.max = value;
    }
    , inputTheme: 'primary'
    , datetimeObj: null // 时间对象格式 {year: , month: ...}
    , done: null // 控件选择完毕后的回调，点击重置/现在/确定也均会触发
    , clear: null // 日期时间重置后的回调
    , now: null // 日期时间点击现在后的回调
    , confirm: null // 日期时间改变后的回调
    , hint: null // 误操作时提示
  };

  // 初始准备 合并默认配置与用户配置 生成input默认格式 绑定事件
  Class.prototype.init = function () {
    var that = this
    ,options = that.config
    ,format = {
      year: 'yyyy' // 尚不支持
      ,month: 'yyyy-MM' // 尚不支持
      ,date: 'yyyy-MM-dd' // 目前支持
      ,datehour: 'yyyy-MM-dd hh' // 目前支持
      ,dateminute: 'yyyy-MM-dd hh:mm' // 目前支持
      ,time: 'hh:mm:ss' // 尚不支持
      ,datetime: 'yyyy-MM-dd hh:mm:ss' // 目前支持
    };
    console.log('init方法调用后的config：', that.config)
    options.elem = $(options.elem);
    options.eventElem = $(options.eventElem);
    if(!options.elem[0]) return;
    var thatTemp = thisModule.that[options.elem.attr('mi-key')]
    console.log("thatTemp && thatTemp.destroy && thatTemp.destroy():",thatTemp, ",thisModule.that:",thisModule.that, ',options.elem.attr(mi-key):',options.elem.attr('mi-key'), ",options.elem:",options.elem); 
    thatTemp && thatTemp.destroy && thatTemp.destroy(); // 销毁上一个实例以便重新渲染

    //若 type 设置非法，则初始化为 date 类型
    if(!format[options.type]){
      window.console && console.error && console.error('midate 类型错误:\''+ options.type + '\' 不支持,已为您重置为date类型')
      options.type = 'date';
    }
    // format不能包含数字
    if(format[options.type] && format[options.type].match(/\d/) != null) {
      throw('【格式化规则】不能包含数字！！')
    }
    // 根据不同 type，初始化默认 format
    if(options.format === format.date){
      options.format = format[options.type] || format.date;
    }
    // 若value非空，判断value是否合法
    if (options.value!=null && typeof options.value != 'string') {
      throw(`当前【value】类型为【${typeof options.value}】,【value】参数应该为【null】或者【字符串】！！`)
    }
    if (typeof options.value == 'string') {
      if (!_common.judgeRQIsValid(options.value, options.type)) {
        throw(`参数【value】值：【${options.value}】为非正确数值，请修改为正确格式的日期值！！`)
      }
    }
    // 判断min/max是否合法
    if(options.min != null && !_common.checkDate(options.min , options.type)) {
      throw('参数【min】请结合参数【type】，修改为合法的日期格式！！')
    }
    if(options.max != null && !_common.checkDate(options.max , options.type)) {
      throw('参数【max】请结合参数【type】，修改为合法的日期格式！！')
    }
    if (options.min != null) {
      options.minObj = {year: (options.min.split(' '))[0].split('-')[0], month: (options.min.split(' '))[0].split('-')[1], day: (options.min.split(' '))[0].split('-')[2], hour: (options.min.split(' '))[1] != undefined ? (options.min.split(' '))[1].split(':')[0] : undefined, minute: (options.min.split(' '))[1] != undefined ? (options.min.split(' '))[1].split(':')[1] : undefined, second: (options.min.split(' '))[1] != undefined ? (options.min.split(' '))[1].split(':')[2] : undefined};
      //console.log("options.minObj:",options.minObj,",options.maxObj:",options.maxObj)
    }
    if (options.max != null) {
      options.maxObj = {year: (options.max.split(' '))[0].split('-')[0], month: (options.max.split(' '))[0].split('-')[1], day: (options.max.split(' '))[0].split('-')[2], hour: (options.max.split(' '))[1] != undefined ? (options.max.split(' '))[1].split(':')[0] : undefined, minute: (options.max.split(' '))[1] != undefined ? (options.max.split(' '))[1].split(':')[1] : undefined, second: (options.max.split(' '))[1] != undefined ? (options.max.split(' '))[1].split(':')[2] : undefined};
    }
    // 设置了一周的开始是周几，此处做一个控制
    if (options.weekStart) {
      if (!/^[0-6]$/.test(options.weekStart)) {
        var lang = that.lang();
        options.weekStart = lang.weeks.indexOf(options.weekStart);
        if (options.weekStart === -1) options.weekStart = 0;
      }
    }
    $(`<div id="mi-date-${options.elem.attr('id')}" class="mi-date-box${options.inputTheme == 'primary' ? ' mi-date-box-primary' : ''}">
      <span class="iconfont icon-riqi"></span>
      ${options.elem.prop("outerHTML")}
    </div>`).replaceAll($(options.elem))
    options.elem = $('#'+options.elem.attr('id'));
    options.elem.val(options.value == null ? ( _common.formatDefaultDate(options.format)) : _common.formatDate((options.value.replace(/\D/g,'')), options.format));

    //设置唯一KEY
    options.elem.attr('mi-key', that.index);
    options.eventElem.attr('mi-key', that.index);
    
    that.elemID = 'mi-date'+ options.elem.attr('mi-key');
    that.events();
    console.log("init方法调用后的options:",options)
  };

  //日历表头生成 日一二三四五六
  Class.prototype.getCalendarTitle = function (weekStart) {
    weekStart = /^[0-6]$/.test(weekStart) == false ? 0 : weekStart;
    let weekArr = '日一二三四五六'.split('');
    let week1 = weekArr.slice(0, weekStart);
    let week2 = weekArr.slice(weekStart);
    return week2.concat(week1).join('');
  }

  //控件主体渲染 生成日期选择面板
  Class.prototype.render = function () {
    var that = this;
    var options = that.config;
    //$(options.elem).val(that.formatDefaultDate(options.format));
     //记录当前执行的实例索引
     midate.thisId = options.id;
    console.log("render调用：", ",this:",this, ",options.elem:",options.elem, ",options.elem[0]:",options.elem[0],',options:',options,"midate:",midate)
  };

  // 生成日期选择面板
  Class.prototype.renderPanel = function () {
    var that = this;
    var options = that.config;
    midate.thisPanelId = options.id;
    console.log("renderPanel调用：",options.elem.offset()/*, ",this:",this, ",options.elem:",options.elem, ",options.elem[0]:",options.elem[0],',options:',options,"midate:",midate*/);
    let top = options.elem.offset().top + 35;
    let left = options.elem.offset().left;
    let panelStr = `<div id="mi-date${options.id}" data-type="${options.type}" class="midate-wrapper" style="top:${top}px;left:${left}px;">
    <div class="midate-header">
      <span class="midate-prev-y"></span>
      <span class="midate-prev-m"></span>
      <input class="midate-now-y" onfocus="event.target.select();" onkeypress="if(this.value.length<4||window.getSelection().toString()!=''){return event.keyCode>=48&&event.keyCode<=57}else{return false}" onblur="if(isNaN(this.value)) { this.value = 1} else { this.value = this.value * 1 > 9999 ? 9999 : this.value }">年
      <input class="midate-now-m" onfocus="event.target.select();" onkeypress="if(this.value.length<2||window.getSelection().toString()!=''){return event.keyCode>=48&&event.keyCode<=57}else{return false}" onkeyup="if(isNaN(this.value)) { this.value = 1} else { this.value = this.value * 1 > 12 ? 12 : this.value }">月
      <!-- <span id="nowDate"></span> -->
      <span class="midate-next-m"></span>
      <span class="midate-next-y"></span>
    </div>
    <div class="midate-week-line"></div>
    <div class="midate-date-wrap"></div>
    <div class="midate-time">
      <div class="midate-time-inputs">
        <span class="iconfont icon-shizhong" style="color: #000;"></span>
        <input class="midate-time-inputs-hour" type="number" value="0" min="0" max="23" onfocus="event.target.select();" onkeypress="if(this.value.length<2||window.getSelection().toString()!=''){return event.keyCode>=48&&event.keyCode<=57}else{return false}" onblur="if(this.value*1>this.max*1){this.value = this.max*1}else{this.value = this.value*1}"><span class="midate-time-span-hour">时</span>
        <input class="midate-time-inputs-minute" type="number" value="0" min="0" max="59" onfocus="event.target.select();" onkeypress="if(this.value.length<2||window.getSelection().toString()!=''){return event.keyCode>=48&&event.keyCode<=57}else{return false}" onblur="if(this.value*1>this.max*1){this.value = this.max*1}else{this.value = this.value*1}"><span class="midate-time-span-minute">分</span>
        <input class="midate-time-inputs-second" type="number" value="0" min="0" max="59" onfocus="event.target.select();" onkeypress="if(this.value.length<2||window.getSelection().toString()!=''){return event.keyCode>=48&&event.keyCode<=57}else{return false}" onblur="if(this.value*1>this.max*1){this.value = this.max*1}else{this.value = this.value*1}"><span class="midate-time-span-second">秒</span>
      </div>
    </div>
    <div class="midate-btns">
      <span class="midate-btn-clear">重置</span>
      <span class="midate-btn-now">现在</span>
      <span class="midate-btn-confirm">确定</span>
    </div>
  </div>`; 
  that.remove(Class.thisElemDate);
  $(document.body).append(panelStr);
  // 绑定事件
  $(`#mi-date${options.id}`).on('click', function(event) {
    if ($(event.target).hasClass('midate-prev-y') || $(event.target).hasClass('midate-next-y') || $(event.target).hasClass('midate-prev-m') || $(event.target).hasClass('midate-next-m')) { // 上年/下年/上月/下月
      let year = $(`#mi-date${options.id}`).children('.midate-header').children('.midate-now-y').val() * 1;
      let month = $(`#mi-date${options.id}`).children('.midate-header').children('.midate-now-m').val() * 1 - 1;

      $(event.target).hasClass('midate-prev-y') ? _common.updateCalendar(year - 1, month, undefined, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that)
      : $(event.target).hasClass('midate-next-y') ?  _common.updateCalendar(year + 1, month, undefined, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that)
      : $(event.target).hasClass('midate-prev-m') ? _common.updateCalendar(year, month - 1, undefined, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that)
      : _common.updateCalendar(year, month + 1, undefined, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that)
    } else if ($(event.target).hasClass('midate-now-y') || $(event.target).hasClass('midate-now-m')) { // 点击当前年/月
      
      if ($(event.target).hasClass('midate-now-m')) { // 月份面板生成
        _common.generalMonthPanel(`mi-date${options.id}`, that)
      } else { // 年份面板生成
        let year = $(`#mi-date${options.id}`).children('.midate-header').children('.midate-now-y').val() * 1;
        _common.generalYearPanel(year, `mi-date${options.id}`, 0, false, that);
      }
    } else if ($(event.target).parents('.midate-year-panel').length != 0) { // 年份面板
      if ($(event.target).hasClass('midate-year-panel-arrow-left')) { // 年份左翻
        let year = $(event.target).parents('.midate-year-panel').children('table').first().children('tbody').children('tr').first().children('td').first().text() * 1;
        console.log("左方！！！！:", year)
        _common.generalYearPanel(year - 10, `mi-date${options.id}`, 1, true, that);
      } else if ($(event.target).hasClass('midate-year-panel-arrow-right') && !$(event.target).hasClass('no-allow-click')) { // 年份右翻
        let year = $(event.target).parents('.midate-year-panel').children('table').first().children('tbody').children('tr').first().children('td').first().text() * 1;
        _common.generalYearPanel(year + 10, `mi-date${options.id}`, 1, true, that);
      } else if ($(event.target).hasClass('midate-year-panel-close')) { // 关闭年份面板
        $(event.target).parents('.midate-year-panel').remove();
      } else { // 年份选中
        console.log("年份选中！！！！:", $(event.target).text())
        if ($(event.target).text() != '') {
          if ($(event.target).hasClass('mi-date-disabled')) return false;
          $(`#mi-date${options.id} .midate-header .midate-now-y`).val($(event.target).text());
          $(`#mi-date${options.id} .midate-header .midate-now-y`).trigger('change');
          $(`#mi-date${options.id} .midate-year-panel`).remove();
        }
      }
    } else if ($(event.target).parents('.midate-month-panel').length != 0) { // 月份面板
      if ($(event.target).hasClass('mi-date-disabled')) return false;
      $(`#mi-date${options.id} .midate-header .midate-now-m`).val($(event.target).text().replace(/\D/,''));
      $(`#mi-date${options.id} .midate-header .midate-now-m`).trigger('change');
      $(`#mi-date${options.id} .midate-month-panel`).remove();
    } else if ($(event.target).parents('.midate-date-wrap').length != 0) { // 天份面板
      if ($(event.target).hasClass('mi-date-disabled')) {
        return false;
      }
      if (options.min && $(event.target).attr('mi-ymd') == options.min.split(' ')[0]) {
        $(`#mi-date${options.id} .midate-time-inputs-hour`).val()*1 < options.minObj.hour*1 ? $(`#mi-date${options.id} .midate-time-inputs-hour`).val(options.minObj.hour*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-minute`).val()*1 < options.minObj.minute*1 ? $(`#mi-date${options.id} .midate-time-inputs-minute`).val(options.minObj.minute*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-second`).val()*1 < options.minObj.second*1 ? $(`#mi-date${options.id} .midate-time-inputs-second`).val(options.minObj.second*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-hour`).attr('max', options.minObj.hour*1);
        $(`#mi-date${options.id} .midate-time-inputs-minute`).attr('max', options.minObj.minute*1);
        $(`#mi-date${options.id} .midate-time-inputs-second`).attr('max', options.minObj.second*1);
      } else if (options.max && $(event.target).attr('mi-ymd') == options.max.split(' ')[0]) {
        $(`#mi-date${options.id} .midate-time-inputs-hour`).val()*1 > options.maxObj.hour*1 ? $(`#mi-date${options.id} .midate-time-inputs-hour`).val(options.maxObj.hour*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-minute`).val()*1 > options.maxObj.minute*1 ? $(`#mi-date${options.id} .midate-time-inputs-minute`).val(options.maxObj.minute*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-second`).val()*1 > options.maxObj.second*1 ? $(`#mi-date${options.id} .midate-time-inputs-second`).val(options.maxObj.second*1) : '';
        $(`#mi-date${options.id} .midate-time-inputs-hour`).attr('max', options.maxObj.hour*1);
        $(`#mi-date${options.id} .midate-time-inputs-minute`).attr('max', options.maxObj.minute*1);
        $(`#mi-date${options.id} .midate-time-inputs-second`).attr('max', options.maxObj.second*1);
      } else {
        $(`#mi-date${options.id} .midate-time-inputs-hour`).attr('max', 23);
        $(`#mi-date${options.id} .midate-time-inputs-minute`).attr('max', 59)
        $(`#mi-date${options.id} .midate-time-inputs-second`).attr('max', 59)
      }
      if ($(event.target).hasClass('midate-day-prev') || $(event.target).hasClass('midate-day-next')) { // 上个月 下个月天份点击跳转
        console.log("$(event.target):",$(event.target).attr('mi-ymd'))
        let year = $(event.target).attr('mi-ymd').split('-')[0] * 1;
        let month = ($(event.target).attr('mi-ymd')).split('-')[1] * 1 - 1;
        let day = $(event.target).attr('mi-ymd').split('-')[2] * 1;
        _common.updateCalendar(year, month, day, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that);
      } else if ($(event.target).hasClass('midate-date-item')) { // 本月
        $(`#mi-date${options.id} .midate-date-wrap .midate-date-item`).removeClass('mi-this');
        $(event.target).addClass('mi-this');
      }
    } else if ($(event.target).hasClass('midate-btn-clear')) { // 重置点击
      options.elem.val(_common.formatDefaultDate(options.format));
      that.remove(Class.thisElemDate);
      options.value = options.datetimeObj = null;
      that.callBackFactory(null, 'clear');
    } else if ($(event.target).hasClass('midate-btn-now')) { // 现在点击
      let nowDate = new Date();
      let year = nowDate.getFullYear();
      let month = nowDate.getMonth();
      let day = nowDate.getDate();
      let hour = nowDate.getHours();
      let minute = nowDate.getMinutes();
      let second = nowDate.getSeconds();
      that.callBackFactory(null, 'now');
      month = month + 1;
      let ymdhms = year + (month < 10 ? '0' + month : month) + (day < 10 ? '0' + day : day) + (hour < 10 ? '0' + hour : hour) + (minute < 10 ? '0' + minute : minute) + (second < 10 ? '0' + second : second);
      let formatV = _common.formatDate(ymdhms, that.config.format); 
      if ((options.min && formatV.replace(/[^\d]/g, '') < options.min.replace(/[^\d]/g, '')) || options.max && formatV.replace(/[^\d]/g, '') > options.max.replace(/[^\d]/g, '')) {
        that.hint(`#mi-date${options.id}`, '当前日期不在限定日期内！')
        //console.error('不在限定日期内')
        return false;
      }
      options.elem.val(formatV);
      that.remove(Class.thisElemDate);
    } else if ($(event.target).hasClass('midate-btn-confirm')) { // 确定点击
      let hour = $(`#mi-date${options.id} .midate-time-inputs-hour`).length != 0 ? $(`#mi-date${options.id} .midate-time-inputs-hour`).val() : '';
      let minute = $(`#mi-date${options.id} .midate-time-inputs-minute`).length != 0 ? $(`#mi-date${options.id} .midate-time-inputs-minute`).val() : '';
      let second = $(`#mi-date${options.id} .midate-time-inputs-second`).length != 0 ? $(`#mi-date${options.id} .midate-time-inputs-second`).val() : '';
      let hms = (hour != '' && hour < 10 ? '0' + hour : hour) + (minute != '' && minute < 10 ? '0' + minute : minute) + (second != '' && second < 10 ? '0' + second : second);
      let ymd = $(`#mi-date${options.id} .mi-this`).attr('mi-ymd').split('-');
      let formatV = _common.formatDate((ymd.join(''))+hms, that.config.format); 
      if ((options.min && formatV.replace(/[^\d]/g, '') < options.min.replace(/[^\d]/g, '')) || options.max && formatV.replace(/[^\d]/g, '') > options.max.replace(/[^\d]/g, '')) {
        //console.error('不在限定日期内')
        that.hint(`#mi-date${options.id}`, '请在限定日期内选则一个合法日期！')
        return false;
      }
      options.elem.val(formatV);
      options.value = formatV;
      options.datetimeObj = {year: ymd[0], month: ymd[1], day: ymd[2], hour: hour, minute: minute, second: second}
      that.remove(Class.thisElemDate);
      that.callBackFactory(null, 'confirm');
    }
    that.callBackFactory(null, 'done');
  })

  // change事件 年/月change
  $('.midate-now-y,.midate-now-m').on('change', function() {
    console.log("面板更新触发！！！")
    let year = $(this).parents(`#mi-date${options.id}`).children('.midate-header').children('.midate-now-y').val() * 1;
    let month = $(this).parents(`#mi-date${options.id}`).children('.midate-header').children('.midate-now-m').val() * 1 - 1;
    _common.updateCalendar(year, month, undefined, -1, -1, -1, options.weekStart, `mi-date${options.id}`, that)
  })
  //$('.')

  Class.thisElemDate = options.id;
  console.log("that:",that);
  _common.insertCalendarTitle(options.weekStart);
  if (options.elem.val().replace(/[^\d]/g, '') == _common.formatDefaultDate(options.format)) {
    _common.updateCalendar(undefined, undefined, undefined, undefined, undefined, undefined, options.weekStart, `mi-date${options.id}`, that);
  } else {
    let rqv = options.elem.val().replace(/[^\d]/g, '');
    console.log("rqv:::::::::::::::",rqv)
    let year = rqv.substring(0,4) * 1 != 0 ? rqv.substring(0,4) * 1 : options.minObj != undefined ? options.minObj.year * 1: undefined;
    let month = year != undefined && rqv.substring(4,6) * 1 != 0 ? rqv.substring(4,6) * 1 - 1 : options.minObj != undefined ? options.minObj.month * 1 - 1 : undefined;
    let day = year != undefined && month != undefined && rqv.substring(6,8) * 1 != 0 ? rqv.substring(6,8) * 1 : options.minObj != undefined ? options.minObj.day * 1 : undefined;
    let hour = rqv.length >= 10 ? rqv.substring(8,10) * 1 : undefined;
    let minute = rqv.length >= 12 ? rqv.substring(10,12) * 1 : undefined;
    let second = rqv.length >= 14 ? rqv.substring(12,14) * 1 : undefined;
    console.log("渲染：：",year, month, day, hour, minute, second,year != undefined && rqv.substring(5,6) * 1 != 0 )
    _common.updateCalendar(year, month, day, hour, minute, second, options.weekStart, `mi-date${options.id}`, that);
  }
  
  }

   //绑定的元素事件处理 日期图标点击时渲染面板
   Class.prototype.events = function(){
    var that = this
    var options = that.config

    //if(!options.elem[0] || options.elem[0].eventHandler) return;

    // 输入框事件绑定
    var inputEvent = function(){
      // 已经打开的面板避免重新渲染
      console.log("event调用：", ",midate.thisId === options.id:",midate.thisId === options.id)
      if(midate.thisId === options.id) return;
      that.bindElem = this;
      that.render();
    }

    // 日期面板
    var showEvent = function() {
      // 已经打开的面板避免重新渲染
      if(midate.thisPanelId === options.id) return;
      //that.bindElem = this;
      that.renderPanel();
    }

    // 触发值改变的事件
    var changeValueEvent = function(event){
      console.log("changeValueEvent调用:",", thisModule.that：",  thisModule.that, ", midate:",midate,", midate.thisId:",midate.thisId)
      if(!midate.thisId) return;
      var that = thisModule.getThis(midate.thisId);

      console.log("原始值：",$(that.bindElem).val())
      let rqV;
      switch(event.type) {
        case 'paste':
          console.log("paste调用:",",赋值：：：",event.originalEvent.clipboardData.getData('text/plain'),that.config.format[4])
          rqV = event.originalEvent.clipboardData.getData('text/plain').replace(/[^\d]/g, '');
          console.log("rqv",rqV,",日期值是否正确：",_common.judgeRQIsValid(rqV, that.config.type))
          let splitStr = that.config.format[4] == undefined ? '-' : that.config.format[4];
          if (_common.judgeRQIsValid(rqV, that.config.type)) {
            let pasteStr = event.originalEvent.clipboardData.getData('text/plain');
            let formatV = _common.formatDate(rqV, that.config.format); // 格式化后的值
            console.log("日期粘贴：", rqV, ",日期格式化：",formatV,$(that.bindElem).val())
            setTimeout(() => {
              $(that.bindElem).val(formatV);
              console.log("格式化后赋值：",formatV,that.config.elem.val())
            }, 10)
          } else {
            return false;
          }
          break;
        case 'keydown':
          // break;
          let nowPos = _common.getCursortPosition(event);
          console.log("keydown调用:",",event.key:",event.key, ",event.keyCode:",event.keyCode,",nowPos:",nowPos,",$(that.bindElem).val():",$(that.bindElem).val(), ",that.config:", that.config)
          rqV = $(that.bindElem).val().split('');
          if (event.key == 'Backspace') { // 删除键删除时置为0或1
            if (nowPos.start - 1 >= 0) {
              nowPos.start = nowPos.start-1;
              nowPos.end = nowPos.end-1;
              if (/\d/.test(rqV[nowPos.start])) {
                rqV[nowPos.start] = 0;
                $(that.bindElem).val(rqV.join(''))
              }
              _common.setInputPosition($(that.bindElem)[0], nowPos)
            }
            event.preventDefault();
          } else if (/\d/.test(event.key)) {
            if (/\d/.test(rqV[nowPos.start])) {
              console.log("当前位置：",nowPos.start,",当前位置字符：", rqV[nowPos.start])
              rqV[nowPos.start] = event.key;
              console.log("匹配数字")
              let rqNextJumpReturnObj = _common.rqNextJump(rqV.join(''), nowPos, event.key)
              if (rqNextJumpReturnObj == -1) return false;
              let nextJump = rqNextJumpReturnObj.nextJumpPosition; // 下一跳距离
              rqV = rqNextJumpReturnObj.rqvArr;
              $(that.bindElem).val(rqV)
              nowPos.start = nextJump;
              nowPos.end = nextJump;
              nextJump != -1 ?_common.setInputPosition($(that.bindElem)[0], nowPos) : '';
            }
            event.preventDefault();
          } else if (['ArrowLeft', 'ArrowRight'].includes(event.key) || (event.ctrlKey == true && (event.key == 'c'|| event.key == 'v' || event.key == 'a'))) { // 左右箭头，粘贴、全选、复制以外
            console.log("event.key；",event.key);
            if (['ArrowLeft', 'ArrowRight'].includes(event.key)) {
              event.preventDefault();
              let nextJump = _common.rqNextJump_RL(rqV.join(''), nowPos, event.key);
              nextJump != -1 ?_common.setInputPosition($(that.bindElem)[0], {start: nextJump, end:nextJump}) : '';
            }
          } else {
            event.preventDefault();
          }
          break;
        case 'input': // 去除中文输入法可以输入中文
          event.preventDefault();
          rqV = $(that.bindElem).val().split('');
          //if (!/\d/.test(rqV[( _common.getCursortPosition(event)).start])) return;
          setTimeout(() => {
            console.log("触发input:", rqV,", that.config.format:", that.config.format)
            let formatV =  _common.stringToFormatDate($(that.bindElem).val().replace(/[^\d|\/]/g, ''), that.config.format);
            $(that.bindElem).val(formatV);
          },1)
          break;
        case 'blur': // 失焦
          let rqv = $(that.bindElem).val().replace(/[^\d]/g, '');
          if(rqv*1 == 0) return false
          let tips;
          let top = options.elem.offset().top + 36;
          let left = options.elem.offset().left + options.elem.outerWidth()/2;
          if (!_common.checkDate(_common.stringToFormatDate(rqv, midate.format[options.type]), options.type)) { // 日期不合法提示
            console.log("日期不合法：",_common.stringToFormatDate(rqv, midate.format[options.type]), options.type,midate.format[options.type],options.elem.width()/2)
            tips = '日期不合法,，已重置'
          } else if (rqv*1 > 0 && (options.min!=null || options.max!=null)) {
            if (( (options.maxrqv*1 > (options.max.replace(/[^\d]/g, '')*1)) || (options.min && rqv*1 < (options.min.replace(/[^\d]/g, '')*1)))) {
              console.log('不在有效日期或时间范围内')
              tips = (options.min!=null && options.max!=null) ? `时间范围应在${options.min}至${options.max}之间，已重置`
              : options.min!=null ? `时间范围应大于${options.min}，已重置`
              : options.max!=null ? `时间范围应小于${options.max}，已重置` : ''
            }
          }
          if (tips != undefined) {
            options.elem.val( _common.formatDefaultDate(options.format))
            $(document.body).append(`<div class="mi-date-tooltip mi-tooltip" style="top:${top}px;left:${left}px;transform:translateX(-50%);">${tips}</div>`);
            setTimeout(() => {
              $('.mi-date-tooltip').remove();
            }, 3000)
          }
          console.log("日期值???????????????????????：",rqv)
          break;
      }
    }
    //绑定呼出控件事件
    options.elem.on('focus', inputEvent);
    options.elem.on('paste keydown input blur', changeValueEvent)
    options.elem[0].eventHandler = true;
    options.elem.parent().children('.icon-riqi').on('click', showEvent);
    options.eventElem.on('focus', inputEvent);
    console.log("监听事件元素：",",options.elem:",options.elem, ",options.eventElem:",options.eventElem)
   
    that.destroy = function () {
      that.remove();
      options.elem.off('focus', inputEvent);
      options.elem.off('paste keydown input blur', changeValueEvent);
      options.elem.removeAttr('mi-key');
      options.elem[0].eventHandler = false;
      options.elem.parent().children('.icon-riqi').off('click', showEvent);
      // options.eventElem.off('focus', showEvent);
      // options.eventElem.removeAttr('mi-key');

      delete thisModule.that[options.id];
    }
  };

  // 回调函数加工
  Class.prototype.callBackFactory = function(param, type){
    var that = this
    ,options = that.config
    param = param || [options.value, options.datetimeObj];
    typeof options[type || 'done'] === 'function' && options[type || 'done'].apply(options, param);
    return that;
  };

// common对象 保存一些公用方法
const _common = {
    /**
  * @method  获取光标位置
  * @param {Object} [e] 当前事件对象
  */
     getCursortPosition : function(e) {
      let pos = {start:0, end: 0};
      pos = _common.getPosition(e.target);
      return pos;
    }
    ,
    // 输入框获取光标位置
    getPosition : function(element) {
      let cursorPos = {start:0, end: 0};
      // IE
      if (document.selection) {
        throw("不支持ie")
      } else if (element.selectionStart || element.selectionStart == '0') {
        cursorPos = {start:element.selectionStart, end: element.selectionEnd};
      }
      return cursorPos;
    }
    ,
    // 设置光标位置 input/textarea
    setInputPosition : function(textDom, pos) {
      console.log("设置光标位置：", pos,",textDom:",textDom,textDom.createTextRange,textDom.setSelectionRange)
      if (textDom.setSelectionRange) {
          // IE Support
          textDom.focus();
          textDom.setSelectionRange(pos.start, pos.end);
      } else if (textDom.createTextRange) {
          // Firefox support
          var range = textDom.createTextRange();
          range.collapse(true);
          range.moveEnd('character', pos.start);
          range.moveStart('character', pos.end);
          range.select();
          console.log("光标！！！！")
      }
    }
    ,
    /**
    * @method 设置最小、最大日期
    * @param {string} [datestring] 日期值 yyyy-MM-dd hh:mm:ss
    */
    setMinMax: function(datestring) {
      let obj = {};
      let ymd = datestring.split(' ')[0];
      let hms = datestring.split(' ')[1] == undefined ? '00:00:00' : datestring.split(' ')[1];
      obj.year = ymd.split('-')[0];
      obj.month = ymd.split('-')[1];
      obj.day = ymd.split('-')[2];
      obj.hour = hms.split(':')[0];
      obj.minute = hms.split(':')[1] == undefined ? '00' : hms.split(':')[1];
      obj.second = hms.split(':')[2] == undefined ? '00' : hms.split(':')[2];
      return obj
    },
    /**
    * @method 日期是否在范围内
    * @param {string} [datestring] 日期值 yyyy-MM-dd
    * @param {string} [min] 最小日期
    * @param {string} [max] 最大日期
    */
     ymdIsInRange: function(datestring, min, max) {
      let returnFlag = true;
      min != null && datestring >= min ? '' : returnFlag = false; 
      max != null && datestring <= max ? '' : returnFlag = false; 
      return returnFlag;
    }
    ,
    /**
    * @method 检查日期是否有效
    * @param {string} [datestring] 日期值
    * @param {string} [type] 日期格式类型 可选值【date|datetime|datehour|dateminute】
    */
    checkDate : function(datestring, type) {
      if (typeof datestring != 'string') return false;
      var reg_date = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/;
      reg_datetime = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\s+([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$/
      reg_datehour = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\s+([01][0-9]|2[0-3])$/
      reg_dateminute = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)\s+([01][0-9]|2[0-3]):[0-5][0-9]$/
      let returnFlag = false;
      switch(type) {
        case 'date':
          returnFlag = datestring.match(reg_date);
          break;
        case 'datetime':
          returnFlag = datestring.match(reg_datetime);
          break;
        case 'datehour':
          returnFlag = datestring.match(reg_datehour);
          break;
        case 'dateminute':
          returnFlag = datestring.match(reg_dateminute);
          break;
      }
      return returnFlag != null ? true : false;
    },
    // 默认日期格式
    formatDefaultDate : function (format = 'yyyy-MM-dd hh:mm:ss') {
      
      var o = {
        "M+": 0, // month
        "d+": 0, // day
        "h+": 0, // hour
        "m+": 0, // minute
        "s+": 0, // second
      };
      var matchArr = []; // 辅助变量
      if (/(y+)/.test(format)) {
        matchArr = format.match(/(y+)/)
        format = format.replace(matchArr[0], ("0000").substring(4 - matchArr[0].length));
      }
    
      for (var k in o) {
        if (new RegExp("(" + k + ")").test(format)) {
          matchArr = format.match(new RegExp("(" + k + ")"))
          format = format.replace(matchArr[0], matchArr[0].length == 1 ? o[k] : ("00" + o[k]).substring(("" + o[k]).length));
        }
      }
      return format;
    }
    ,
    /**
    * @method 非合法日期格式化 如00000101 主要用于防止中文输入时的使用
    * @param {number} [type] year date datetime
    * @param {string} [dateString] 日期值 eg: 20120606121212
    * @param {string} [format] 日期格式 yyyy-MM-dd hh:mm:ss
    */
     stringToFormatDate : function (dateString, format = 'yyyy-MM-dd hh:mm:ss') {
      let formatArr = format.split('');
      let returnV = '';
      let index = 0;
      formatArr.forEach((v, i) => {
        if (!/[y|M|d|h|m|s]/.test(v)) {
          returnV += v;
        } else {
          returnV += dateString[index];
          index++;
        }
      })
      return returnV;
    }
    ,
    /**
    * @method 日期格式化
    * @param {number} [type] year date datetime
    * @param {string} [dateString] 日期值 eg: 20120606121212
    * @param {string} [format] 日期格式 yyyy-MM-dd hh:mm:ss
    */
     formatDate : function(dateString, format = 'yyyy-MM-dd hh:mm:ss') {
      var year = dateString.substring(0, 4) * 1;
      var month = dateString.substring(4, 6) * 1 > 1 ? dateString.substring(4, 6) * 1 - 1 : 0;
      var day = dateString.substring(6, 8) * 1;
      var hours = dateString.substring(8, 10) * 1;
      var minutes = dateString.substring(10, 12) * 1;
      var seconds = dateString.substring(12, 14) * 1;
      var date = new Date(year, month, day, hours, minutes, seconds);
      var o = {
        "M+": date.getMonth() + 1, // month
        "d+": date.getDate(), // day
        "h+": date.getHours(), // hour
        "m+": date.getMinutes(), // minute
        "s+": date.getSeconds(), // second
      };
      var matchArr = []; // 辅助变量
      if (/(y+)/.test(format)) {
        matchArr = format.match(/(y+)/)
        format = format.replace(matchArr[0], (date.getFullYear() + "").substring(4 - matchArr[0].length));
      }
  
      for (var k in o) {
        if (new RegExp("(" + k + ")").test(format)) {
          matchArr = format.match(new RegExp("(" + k + ")"))
          format = format.replace(matchArr[0], matchArr[0].length == 1 ? o[k] : ("00" + o[k]).substring(("" + o[k]).length));
        }
      }
      return format;
    }
    ,
    /**
    * @method  获取年份天数
    * @param {number} [year] 年份
    * @param {number} [month] 月份
    */
     getDayByYear : function(year, month) {
      if(year == undefined)
        return
      let isRN = ((year % 4 == 0) && (year % 100 != 0)) || ((year % 100 == 0) && (year % 400 == 0) )
      let days = 30
      switch(month*1) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
          days = 31
          break;
        case 2:
          isRN == true ? days = 29 : days = 28
          break;
      }
      console.log("当前月份：",month,",当前天数：",days)
      return days;
    }
    ,
    /**
    * @method  判断日期是否合法
    * @param {number} [rqV] 日期值 eg: 20120606121212
    * @param {number} [type] year month date datetime
    */
     judgeRQIsValid : function(rqV, type) {
      rqV = rqV.replace(/[^\d]/g, '');
      let isValid = false;
      switch(type) {
        case 'year':
          isValid = rqV.length == 4 ? true : false;
          break;
        case 'month':
          isValid = (rqV.length == 6 && rqV.substring(4,6)*1<13) ? true : false;
          break;
        case 'date':
          isValid = (rqV.length == 8 && rqV.substring(4,6)*1<13 && _common.getDayByYear(rqV.substring(1,4)*1,rqV.substring(4,6)*1) >= rqV.substring(6,8)) ? true : false;
          break;
        case 'datehour':
          isValid = (rqV.length == 10 && rqV.substring(4,6)*1<13 && _common.getDayByYear(rqV.substring(1,4)*1,rqV.substring(4,6)*1) >= rqV.substring(6,8) && rqV.substring(8, 10)*1 < 24) ? true : false;
          break;
        case 'dateminute':
          isValid = (rqV.length == 12 && rqV.substring(4,6)*1<13 && _common.getDayByYear(rqV.substring(1,4)*1,rqV.substring(4,6)*1) >= rqV.substring(6,8) && rqV.substring(8, 10)*1 < 24) ? true : false;
          break;
        case 'datetime':
          isValid = (rqV.length == 14 && rqV.substring(4,6)*1<13 && _common.getDayByYear(rqV.substring(1,4)*1,rqV.substring(4,6)*1) >= rqV.substring(6,8) && rqV.substring(8, 10)*1 < 24 && rqV.substring(10, 12)*1 < 60 && rqV.substring(12, 14)*1 < 60) ? true : false;
          break;
      }
      return isValid;
    }
    ,
    /**
    * @method 左右箭头控制下一跳光标位置
    * @param {string} [rqv] 当前日期输入框的值
    * @param {object} [nowPos] 当前光标的位置 eg: {start: 0, end: 0}
    * @param {string} [direction] 下一跳方向 【ArrowRight|ArrowLeft】 默认ArrowRight
    */
     rqNextJump_RL: function(rqv, nowPos, direction = 'ArrowRight') {
      let nextJumpPosition = -1;
      let rqvArr = rqv.split(''); // 日期值分割成数组
      let digitFlag = false; // 数字标识 标识下一跳是否是数字
      console.log("nowPos.start:",nowPos.start)
      if (direction == 'ArrowRight') {
        for(let i = nowPos.start * 1 + 1; i < rqvArr.length; i++) {
          if(/\d/.test(rqvArr[i])) {
            nextJumpPosition = i;
            digitFlag = true;
            console.log("nextJumpPosition：",nextJumpPosition)
            break;
          }
        }
        console.log("?????????",nowPos.start * 1 + 1, rqvArr.length, rqvArr[nowPos.start * 1 + 1])
        digitFlag == false ? nextJumpPosition = rqvArr.length : '';
      } else {
        for(let i = nowPos.start * 1 - 1; i > 0; i--) {
          if(/\d/.test(rqvArr[i])) {
            digitFlag = true;
            nextJumpPosition = i;
            break;
          }
        }
        digitFlag == false ? nextJumpPosition = 0 : '';
      }
      return nextJumpPosition;
    }
    ,
    /**
    * @method  日期 单个数字输入后 下一跳光标位置
    * @param {string} [rqv] 当前日期输入框的值
    * @param {object} [nowPos] 当前光标的位置 eg: {start: 0, end: 0}
    * @param {number|string} [enterValue] 当前输入的值
    */
     rqNextJump : function(rqv, nowPos, enterValue) {
      let nextJumpPosition = -1;
      let rqvArr = rqv.split(''); // 日期值分割成数组
      let rqv_d = rqv.replace(/[^\d]/g, ''); // 只保留数字的日期值
      if (!/\d/.test(rqvArr[nowPos.start])) { // 若当前光标位置不是数字
        console.log("当前光标位置不是数字：",nextJumpPosition)
        return nextJumpPosition;
      }
      let notdigitCount = 0; // 非数字的个数
      let digitPosition = 0; // 当前数字在纯日期数字的位置
      for (let i = 0; i < nowPos.start; i ++) { // 计算当前位置之前有多少个非数字
        if (!/\d/.test(rqvArr[i])) {
          notdigitCount++;
        }
      }
      digitPosition = nowPos.start - notdigitCount;
  
      let testDigitCount = 1; // 匹配数字的次数 用于月份 天数 时间等输入后 ，跳到下一个 比如月份输入4时应该直接跳到天数
      /*处理日期输入值【开始】20120308100822*/
      let justifyV = function(maxV, enterV, arr, minIndex, maxIndex) { // 调整值 比较允许填的最大值与当前输入的值 eg:小时 最大值23 输入值24 此时应调整为最大值23
        if (maxV < enterV) {
          let digitIndex = -1;
          console.log("arr1:",arr)
          arr.some((v, i) => {
            // if (digitIndex > maxIndex) {
            //   return true;
            // }
            /\d/.test(arr[i]) ? digitIndex++ : '';
            if (digitIndex == minIndex) {
              console.log("digitIndex:",digitIndex)
              arr[i] = (maxV+'').split('')[0];
              console.log("minIndex @@@@@@@@@@@@@@@@@@@@@@@@@@@@arr[i]:",arr[i-1],",maxV:",maxV)
            }
            if (digitIndex == maxIndex) {
              console.log("digitIndex:",digitIndex)
              arr[i] = (maxV+'').split('')[1];
              console.log("maxIndex %%%%%%%%%%%%%%%%%%%%%%%%%%%%arr[i]:",arr[i-1],",maxV:",maxV)
              return true;
            }
            console.log(`第${i}个，第${digitIndex}个数字`,'v:', v, 'arr[i]:',arr[i]);
            
          })
          console.log("arr2:",arr)
        }
      }
      if (digitPosition == 4 && enterValue >= 2) { // 处理月份 不能大于12
        rqvArr[nowPos.start] = 0;
        rqvArr[nowPos.start + 1] = enterValue;
        testDigitCount = 2;
      } else if (digitPosition == 4 && enterValue == 1) {
        rqvArr[nowPos.start + 1] > 2 ? rqvArr[nowPos.start + 1] = 0 : '';
      } else if (digitPosition == 5 && enterValue > 2) {
        rqvArr[nowPos.start - 1] = 0;
      } else if (digitPosition == 6 && enterValue > 3) { // 处理日期 特殊日期 28 29 30 31
        rqvArr[nowPos.start] = 0;
        rqvArr[nowPos.start + 1] = enterValue;
        testDigitCount = 2;
      } else if (digitPosition == 6 || digitPosition == 7) {
        let days = _common.getDayByYear(rqv_d.substring(0, 4), rqv_d.substring(4, 6));
        justifyV(days, Number(rqv_d[6] + '' + rqv_d[7]), rqvArr, 6, 7);
      } else if (digitPosition == 8  && enterValue > 2) { // 处理小时 不能大于23
        rqvArr[nowPos.start] = 0;
        rqvArr[nowPos.start + 1] = enterValue;
        testDigitCount = 2;
      } else if (digitPosition == 8 || digitPosition == 9) {
        justifyV(23, Number(rqv_d[8] + '' + rqv_d[9]), rqvArr, 8, 9);
      } else if (digitPosition == 10  && enterValue > 5) { // 处理分钟 不能大于59
        rqvArr[nowPos.start] = 0;
        rqvArr[nowPos.start + 1] = enterValue;
        testDigitCount = 2;
      } else if (digitPosition == 10 || digitPosition == 11) {
        justifyV(59, Number(rqv_d[10] + '' + rqv_d[11]), rqvArr, 10, 11);
      } else if (digitPosition == 12  && enterValue > 5) { // 处理秒钟 不能大于59
        rqvArr[nowPos.start] = 0;
        rqvArr[nowPos.start + 1] = enterValue;
        testDigitCount = 2;
      } else if (digitPosition == 12 || digitPosition == 13) {
        justifyV(59, Number(rqv_d[12] + '' + rqv_d[13]), rqvArr, 12, 13);
      }
      /*处理日期输入值【结束】*/
      // nextJumpPosition = 1;
      let caltestDigitCount = 0; // 计算匹配数字的个数
      for( let i = nowPos.start + 1; i < rqvArr.length; i ++) {
        console.log("计算？？？？？")
        if (/\d/.test(rqvArr[i])) {
          caltestDigitCount++;
          if (caltestDigitCount == testDigitCount) {
            nextJumpPosition = i;
            break; // 终止for循环
          }
        }
      }
      console.log("计算匹配数字个数：", caltestDigitCount,",nowPos.start + 1:",rqvArr[nowPos.start + 1],",/\d/.test(rqvArr[nowPos.start]):",/\d/.test(rqvArr[nowPos.start]),caltestDigitCount == 0 && (/\d/.test(rqvArr[nowPos.start])))
      console.log('nextJumpPosition:', nextJumpPosition,rqvArr.join(''))
      if (caltestDigitCount == 0 && !(/\d/.test(rqvArr[nowPos.start+1]))) nextJumpPosition = rqvArr.length; // 匹配数字的个数为0，说明已输入到最后的日期值 则下一跳地址为最后一个
      return {
        nextJumpPosition: nextJumpPosition,
        rqvArr: rqvArr.join('')
      };
    }
    ,
    /**
    * @method  判断平闰年
    * @param {number} [year]
    */
     isLeapYear : function(year) {
      return (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
    }
    ,
    /**
    * @method  获取每个月的日期
    * @param {number} [year]
    * @param {number} [month]
    */
     getMonthCount : function(year, month) {
      let arr = [
        31, null, 31, 30, 
        31, 30, 31, 31,
        30, 31, 30, 31
      ];
      let count = arr[month] || (_common.isLeapYear(year) ? 29 : 28);
      return Array.from(new Array(count), (item, index) => index + 1);
    }
    ,
    /**
    * @method  获得某年某月的 1号 是星期几
    * @param {number} [year]
    * @param {number} [month]
    */
     getWeekday : function(year, month) {
      let date = new Date(year, month, 1);
      return date.getDay();
    }
    ,
    /**
    * @method  获得【日历面板第一行】上个月天数占据多少个
    * @param {number} [year]
    * @param {number} [month]
    * @param {number} [weekStart]
    */
     getPreMonthDaysByWeekStart : function(year, month, weekStart) {
      return _common.getWeekday(year, month) >= weekStart ? _common.getWeekday(year, month) - weekStart : _common.getWeekday(year, month) + 7 - weekStart
    }
    ,
    /**
    * @method  获得上个月的天数
    * @param {number} [year]
    * @param {number} [month]
    */
     getPreMonthCount : function(year, month) {
      if (month === 0) {
        return _common.getMonthCount(year - 1, 11);
      } else {
        return _common.getMonthCount(year, month - 1);
      }
    }
    ,
    /**
    * @method  获得下个月的天数
    * @param {number} [year]
    * @param {number} [month]
    */
     getNextMonthCount : function(year, month) {
      if (month === 11) {
        return _common.getMonthCount(year + 1, 0);
      } else {
        return _common.getMonthCount(year, month + 1);
      }
    }
    ,
    /**
    * @method  生成日历表头
    * @param {number} [weekStart]
    */
    getCalendarTitle : function(weekStart) {
      weekStart = /^[0-6]$/.test(weekStart) == false ? 0 : weekStart;
      let weekArr = '日一二三四五六'.split('');
      let week1 = weekArr.slice(0, weekStart);
      let week2 = weekArr.slice(weekStart);
      return week2.concat(week1).join('');
    }
    ,
    /**
    * @method  插入日历表头
    * @param {number} [weekStart]
    */
    insertCalendarTitle : function(weekStart) {
      weekStart == undefined ? weekStart = 1 : '';
      let weekStr = _common.getCalendarTitle(weekStart);
      weekArr = weekStr.split('').map(item => '' + item);
      // 插入星期 dom
      let oFragWeek = document.createDocumentFragment();
      weekArr.forEach(item => {
        let oSpan = document.createElement('span');
        let oText = document.createTextNode(item);
        oSpan.appendChild(oText);
        oSpan.classList.add('midate-week-item');
        oFragWeek.appendChild(oSpan);
      });
      let weekWrap = document.getElementsByClassName('midate-week-line')[0];
      console.log("weekWrap:",weekWrap)
      weekWrap.appendChild(oFragWeek);
    }
    ,
    /**
    * @method  更新日历面板
    * @param {number} [year]
    * @param {number} [month]
    * @param {number} [day]
    * @param {number} [hour] 为-1 时不更新
    * @param {number} [minute] 为-1 时不更新
    * @param {number} [second] 为-1 时不更新
    * @param {number} [weekStart]
    * @param {string} [calendarElemId] 日历id
    * @param {object} [midate]
    */
    updateCalendar : function(year, month, day, hour, minute, second, weekStart, calendarElemId, midate) {
      console.log("year:",year,",month:",month,",day:",day)
      let nowDate = new Date();
      year = year == undefined ? nowDate.getFullYear() : year;
      month = month == undefined ? nowDate.getMonth() : month;
      day = day == undefined ? 1 : day * 1;
      hour = hour == undefined ? 0 : hour;
      minute = minute == undefined ? 0 : minute;
      second = second == undefined ? 0 : second;
      if ( month < 0) {
        year = year - 1;
        month = 11;
      } else if ( month > 11) {
        year = year + 1;
        month = 0;
      }
      console.log("year:",year,",month:",month,",day:",day, ",hour:", hour, ",minute:",minute,",second:",second)
      // 更新一下顶部的年月显示
      $(`#${calendarElemId}`).children('.midate-header').children('.midate-now-y').val(year);
      $(`#${calendarElemId}`).children('.midate-header').children('.midate-now-m').val(month + 1);
      // 更新一下底部的时分秒
      $(`#${calendarElemId} .midate-time-inputs-hour`).attr('max', '23');
      $(`#${calendarElemId} .midate-time-inputs-minute`).attr('max', '59');
      $(`#${calendarElemId} .midate-time-inputs-second`).attr('max', '59');
      let minObj = midate.config.minObj;
      let maxObj = midate.config.maxObj;
      if (minObj && year == minObj.year*1 && month+1 == minObj.month*1 && day == minObj.day*1) {
        $(`#${calendarElemId} .midate-time-inputs-hour`).val()*1 < minObj.hour ? hour = minObj.hour*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-minute`).val()*1 < minObj.minute ? minute = minObj.minute*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-second`).val()*1 < minObj.second ? second = minObj.second*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-hour`).attr('max', minObj.hour);
        $(`#${calendarElemId} .midate-time-inputs-minute`).attr('max', minObj.minute);
        $(`#${calendarElemId} .midate-time-inputs-second`).attr('max', minObj.second);
      } else if (maxObj && year == maxObj.year*1 && month+1 == maxObj.month*1 && day == maxObj.day*1) {
        $(`#${calendarElemId} .midate-time-inputs-hour`).val()*1 > maxObj.hour*1 ? hour = maxObj.hour*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-minute`).val()*1 > maxObj.minute*1 ? minute = maxObj.minute*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-second`).val()*1 > maxObj.second*1 ? second = maxObj.second*1 : '';
        $(`#${calendarElemId} .midate-time-inputs-hour`).attr('max', maxObj.hour);
        $(`#${calendarElemId} .midate-time-inputs-minute`).attr('max', maxObj.minute);
        $(`#${calendarElemId} .midate-time-inputs-second`).attr('max', maxObj.second);
      }
      $(`#${calendarElemId} .midate-time-inputs-hour`).length != 0 && hour != -1 ? $(`#${calendarElemId} .midate-time-inputs-hour`).val(hour) : '';
      $(`#${calendarElemId} .midate-time-inputs-minute`).length != 0 && minute != -1 ? $(`#${calendarElemId} .midate-time-inputs-minute`).val(minute) : '';
      $(`#${calendarElemId} .midate-time-inputs-second`).length != 0 && second != -1 ? $(`#${calendarElemId} .midate-time-inputs-second`).val(second) : '';
      let monthIndex = month; // 当前月
      let dayIndex; // 当前天
      let ymd; // 保存年月日
      // 生成日历数据，上个月剩下的的 x 天 + 当月的 28（平年的2月）或者29（闰年的2月）或者30或者31天 + 下个月的 y 天 = 42
      let res = [];
      let currentMonth = _common.getMonthCount(year, month);
      let preMonth = _common.getPreMonthCount(year, month);
      let nextMonth = _common.getNextMonthCount(year, month);
      let PreMonthDays = _common.getPreMonthDaysByWeekStart(year, month, weekStart);
      if (PreMonthDays == 0) {
        PreMonthDays = -preMonth.length
      }
      // 截取前一个月、后一个月的数据
      let preArr = preMonth.slice(-1 * PreMonthDays)
      let nextArr = nextMonth.slice(0, 42 - currentMonth.length - PreMonthDays);
      console.log("currentMonth:",currentMonth,",preMonth:",preMonth,",nextMonth:",nextMonth)
      console.log("PreMonthDays:",PreMonthDays)
      console.log(",preArr:",preArr,",currentMonth:",currentMonth,",nextArr:",nextArr)
      res = [].concat(preArr, currentMonth, nextArr);
      // 上面经过我本人的测试是没有什么问题，接下来就是更新 dom 的信息的问题
      let hadDom = document.getElementsByClassName('midate-date-item');
      if (hadDom && hadDom.length) {
        let domArr = document.getElementsByClassName('midate-date-item');
        for (let i = 0; i < domArr.length; i++) {
          dayIndex = res.shift();
          domArr[i].innerHTML = dayIndex;
          if (i+1 <= preArr.length) {
            domArr[i].className = 'midate-date-item midate-day-prev'
          } else if (i+1 > (preArr.length + currentMonth.length)) {
            domArr[i].className = 'midate-date-item midate-day-next'
           } else {
            domArr[i].className = 'midate-date-item'
           }
           domArr[i].className == 'midate-date-item midate-day-prev' ? (monthIndex = month) : domArr[i].className == 'midate-date-item' ? (monthIndex = month + 1) : (monthIndex = month + 2);
           ymd = (year < 1000 ? ('0' + year) : year < 100 ? ('00' + year) : year < 10 ? ('000' + year) : year) + "-" + (monthIndex < 10 ? ('0'+monthIndex) : monthIndex) + "-" + (dayIndex < 10 ? ('0'+dayIndex) : dayIndex);
           ((midate.config.min && ymd < midate.config.min.split(' ')[0]) || (midate.config.max && ymd > midate.config.max.split(' ')[0])) ? domArr[i].className = domArr[i].className + ' mi-date-disabled' : '';
           domArr[i].setAttribute('mi-ymd', ymd)
        }
      } else {
        // 如果之前没有结构的话
        let str = '';
        let className = 'midate-date-item';
        for (let i = 0; i < 6; i++) {
          str += '<div class="midate-date-line">';
          for (let j = 0; j < 7; j++) {
            className = ((i*7) + (j+1)) <= preArr.length ? 'midate-date-item midate-day-prev' : ((i*7) + (j+1)) > (preArr.length + currentMonth.length) ? 'midate-date-item midate-day-next' : 'midate-date-item'
            monthIndex = ((i*7) + (j+1)) <= preArr.length ? month : ((i*7) + (j+1)) > (preArr.length + currentMonth.length) ? (month + 2) : (month + 1)
            dayIndex = res.shift();
            ymd = (year < 1000 ? ('0' + year) : year < 100 ? ('00' + year) : year < 10 ? ('000' + year) : year) + "-" + (monthIndex < 10 ? ('0'+monthIndex) : monthIndex) + "-" + (dayIndex < 10 ? ('0'+dayIndex) : dayIndex);
            //console.log("midate.config.min:",midate.config.min,(ymd < midate.config.min || ymd > midate.config.max));
            ((midate.config.min && ymd < midate.config.min.split(' ')[0]) || (midate.config.max && ymd > midate.config.max.split(' ')[0])) ? className = className + ' mi-date-disabled' : '';
            str += `<span class='${className}' mi-ymd='${ymd}'>${dayIndex}</span>`;
            if (j === 6) {
              str += '</div>';
            }
          }
        }
        // document.getElementById(calendarElemId).innerHTML = str;
        $(`#${calendarElemId}`).children('.midate-date-wrap').html(str);
      }
      month = month + 1;
      //console.log("year:",year,",month:",month,",day:",day)
      ymd = (year < 1000 ? ('0' + year) : year < 100 ? ('00' + year) : year < 10 ? ('000' + year) : year) + "-" + (month < 10 ? ('0'+month) : month) + "-" + (day < 10 ? ('0'+day) : day);
      //console.log("ymd高亮：",ymd)
      let hms = (hour < 10 ? ('0'+hour) : hour) + (minute < 10 ? ('0'+minute) : minute) + (second < 10 ? ('0'+second) : second);
      $(`#${calendarElemId} .midate-date-wrap .midate-date-line .midate-date-item[mi-ymd="${ymd}"]`).addClass('mi-this');
      midate.config.datetimeObj = {year: year, month: month, day: day, hour: hour, minute: minute, second: second};
      midate.config.value = _common.formatDate((ymd.replace(/-/g, ''))+hms, midate.config.format);
    }
    ,
    /**
    * @method  生成年份面板
    * @param {number} [year]
    * @param {string} [calendarElemId] 日历id
    * @param {number} [generalType] 面板生成类型 0：表示year值放在第六个位置 1：表示year值放第一个位置
    * @param {object} [midate]
    */
   generalYearPanel : function(year, calendarElemId, generalType = 0, isUpdate = false, midate) {
      if (isUpdate) {$('.midate-year-panel').remove();}
      if ($(`#${calendarElemId}`).children('.midate-year-panel').length!=0) {return;}
      console.log("generalType:",generalType)
      year = year * 1;
      let panel = `<div class="midate-year-panel box-shadow"><table cellspacing="0" cellpadding="3" border="0"><tbody>`;
      let noAllowClick = false;
      let minY = midate.config.min != null ? (midate.config.min.split(' ')[0]).split('-')[0] : null;
      let maxY = midate.config.max != null ? (midate.config.max.split(' ')[0]).split('-')[0] : null;
      // if (generalType = 0) {
        for (let i = year - 5 + (generalType == 0 ? 0 : 5); i < year + (generalType == 0 ? 0 : 5); i ++) {
          panel += `<tr><td class="${i > 9999 ? '' : 'midate-td-hover'}${(( minY && i < minY) || ( maxY && i > maxY)) ? ' mi-date-disabled' : '' }">${i > 9999 ? '' : i}</td><td class="${i + 5 > 9999 ? '' : 'midate-td-hover'}${((minY && i+5 < minY) || (maxY != null && i+5 > maxY)) ? ' mi-date-disabled' : '' }">${i+5 > 9999 ? '' : i+5}</td></tr>`;
          if (i >= 9999 || i + 5 >= 9999) noAllowClick = true;
        }
      // } else {
      //   for (let i = year; i < year + 5; i ++) {
      //     panel += `<tr><td class="${i > 9999 ? '' : 'midate-td-hover'}">${i > 9999 ? '' : i}</td><td class="${i + 5 > 9999 ? '' : 'midate-td-hover'}">${i+5 > 9999 ? '' : i+5}</td></tr>`;
      //     if (i >= 9999 || i + 5 >= 9999) noAllowClick = true;
      //   }
      // }
      panel += `</tbody></table>`;
      let noAllowClickClass = noAllowClick ? ' no-allow-click' : ''
      panel += `<table style="width:94px" cellspacing="0" cellpadding="3" border="0">
      <tbody>
      <tr><td style="width:30px;font-size:18px;" class="iconfont icon-jiantou_xiangzuo_o midate-td-hover midate-year-panel-arrow-left"></td><td style="width:20px;font-size:18px;" class="iconfont icon-guanbi midate-td-hover midate-year-panel-close"></td><td style="width:30px;font-size:18px;" class="iconfont icon-jiantou_xiangyou_o midate-td-hover midate-year-panel-arrow-right${noAllowClickClass}"></td></tr>
      </tbody></table></div>
      `
      $(`#${calendarElemId}`).append(panel);
   }
   ,
   /**
    * @method  生成月份面板
    * @param {string} [calendarElemId] 日历id
    * @param {object} [midate]
    */
    generalMonthPanel : function(calendarElemId, midate) {
      if ($(`#${calendarElemId}`).children('.midate-month-panel').length!=0) {return;}
      let panel = `<table class="midate-month-panel box-shadow" cellspacing="0" cellpadding="3" border="0"><tbody>`;
      let minYM = midate.config.min != null ? (midate.config.min.split(' ')[0]).split('-')[0] + (midate.config.min.split(' ')[0]).split('-')[1] * 1 : null;
      let maxYM = midate.config.max != null ?  (midate.config.max.split(' ')[0]).split('-')[0] + (midate.config.max.split(' ')[0]).split('-')[1] * 1 : null;
      let nowY = $(`#mi-date${midate.config.id}`).children('.midate-header').children('.midate-now-y').val();
      console.log("nowY:",(nowY+(8+1)), minYM, maxYM)
      for (let i = 0; i < 6; i ++) {
        console.log((nowY+(i+1)) , minYM , (nowY+(i+1)) , maxYM, '***********' , (nowY+(i+7)) , minYM , (nowY+(i+7)) , maxYM)
        panel += `<tr><td class="${( (minYM != null && nowY+(i+1))*1 < minYM*1) || ( maxYM != null && (nowY+(i+1))*1 > maxYM*1) ? 'mi-date-disabled' : ''}">${i+1}月</td><td class="${( minYM != null && (nowY+(i+7))*1 < minYM*1) || ( maxYM != null && (nowY+(i+7))*1 > maxYM*1) ? 'mi-date-disabled' : ''}">${i+7}月</td></tr>`;
      }
      panel += `</tbody></table>`;
      $(`#${calendarElemId}`).append(panel);
    }
}

  // 控件提示
  Class.prototype.hint = function(id, text) {
    var that = this;
    $(`${id} .midate-hint-panel`).remove();
    $(id).append(`<div class="midate-hint-panel box-shadow"> ${text} </div>`);
    clearTimeout(that.hintTimer);
    that.hintTimer = setTimeout(function() {
      $(`${id} .midate-hint-panel`).remove();
    }, 3000)
  }

  // 控件移除
  Class.prototype.remove = function (prev) {
    var that = this
    ,options = that.config
    ,elem = $('#mi-date'+ (prev));
    if(!elem[0]) return that;
    delete midate.thisPanelId;
    elem.remove();
    return that;
  };

  // 销毁实例
  midate.destroy = function(id){
    var that = thisModule.getThis(id || midate.thisId);
    if(!that) return;
    return that.destroy();
  };

  //定位算法
  Class.prototype.position = function () {
    
  };

  //记录所有实例
  thisModule.that = {}; //记录所有实例对象

  //获取当前实例对象
  thisModule.getThis = function(id){
    var that = thisModule.that[id];
    if(!that) console.error(id ? (' instance with ID \''+ id +'\' not found') : 'ID argument required');
    return that;
  };

  //核心接口
  midate.render = function (options) {
    var inst = new Class(options);
    // console.log("实例：inst:", inst, ",thisModule.call(inst):",thisModule.call(inst),thisModule.that)
    return thisModule.call(inst);
  };

  midate.remove = function(prev) {
    elem = $('#mi-date'+ (prev));
    if(!elem[0]) return;
    delete midate.thisPanelId;
    elem.remove();
  }
  $(document).on('click', function(event) {
    console.log("documentClick")
    $(event.target).parents('.midate-year-panel').length == 0 && !$(event.target).hasClass('midate-now-y') && !$(event.target).hasClass('midate-year-panel') ? $('.midate-year-panel').remove() : '';
    $(event.target).parents('.midate-month-panel').length == 0 && !$(event.target).hasClass('midate-now-m') && !$(event.target).hasClass('midate-month-panel') ? $('.midate-month-panel').remove() : '';
    if($(event.target).parents('.midate-wrapper').length != 0 || ($(event.target).hasClass('icon-riqi') && $(event.target).parent().children('input').attr('mi-key') != undefined) || $(event.target).hasClass('midate-year-panel') || $(event.target).hasClass('midate-month-panel') || $(event.target).parents('.midate-year-panel').length != 0 || $(event.target).parents('.midate-month-panel').length != 0) {
      console.log("????:",event.target)
      
    } else {
      // console.log("删除：",$(event.target).parents('.midate-wrapper').length, $(event.target),$(event.target).parents('.midate-year-panel'))
      Class.thisElemDate ? midate.remove(Class.thisElemDate) : '';
    }
  })
  window.midate = midate;
})(window, window.document)